Shadow DOM的简单实现
紧接Shadow DOM的简介,介绍关于Shadow DOM的使用方法。
一、例子
<div>
<p>
<span>hello</span>
<span id="box">world</span>
</p>
</div>
像这段HTML,在浏览器中被解析成DOM时,每一个元素就是一个节点,整体构成了一个节点树。
而Shadow DOM可以让我们自己创建节点树,依旧是以上的HTML代码,加上以下的JS代码:
var oBox = document.querySelector('#box');
// 创建 shadow DOM
var shadowRoot = oBox.createShadowRoot();
shadowRoot.innerHTML = 'JavaScript';
首先我们选取了一个id是box的元素作为宿主对象(shadow host),然后使用createShadowRoot()方法给宿主对象增加了一个shadow root的新节点,影子根节点作为影子树的第一个节点,其他的节点都是它的子节点。在控制台下得到以下结构:
当然在页面上发现,world并没有渲染出来,显示的是JavaScript。也就是说宿主对象并没有被渲染,取而代之的是内部的元素。
加入我们想加入其他更多的元素的话,为何不使用原生的创建元素的方式试下呢?
var oBox = document.querySelector('#box');
var shadowRoot = oBox.createShadowRoot();
shadowRoot.textContent = 'JavaScript';
var html = document.createElement('span');
html.textContent = ',HTML';
var css = document.createElement('span');
css.textContent = ',CSS';
shadowRoot.appendChild(html);
shadowRoot.appendChild(css);
// 或者直接
//shadowRoot.innerHTML = 'JavaScript<span>,HTML</span><span>,CSS</span>';
我们使用了原生的createElement()方法创建了元素并添加到影子DOM下,使用了textContent给元素赋值了内容(textContent用法不说啦)。当然,也可用innerHTML一步到位。此时,HTML的结构是这样的,
我们发现Shadow DOM和普通的DOM似乎没有太大的区别,操作普通DOM的方式在这都可以使用。当然,宿主元素还是没有被渲染出来。
二、content 标签
在前面的例子中,我们使用操作普通DOM的API给影子DOM添加了一些DOM元素,并且他们也能成功的在页面上显示出来。
<div id="box">world</div>
<template class="box-template">
hello,<content></content>
</template>
<script type="text/javascript">
var oBox = document.querySelector('#box');
var shadowRoot = oBox.createShadowRoot();
var template = document.querySelector('.box-template');
shadowRoot.appendChild(document.importNode(template.content, true));
</script>
在这,我们使用一个模板标签来创建Shadow DOM中的元素,取代使用繁琐的原生方法。而templete中的
三、select属性
<div id="box">
<span class="html">HTML</span>
<span class="css">CSS</span>
<span class="js">JavaScript</span>
<span>Angular、Node</span>
<p>
<span>Java</span>
<span>PHP</span>
</p>
</div>
<template class="box-template">
<ul>
<li><content select=".html"></content></li>
<li><content select=".css"></content></li>
<li><content select=".js"></content></li>
<li><content select=""></content></li>
</ul>
</template>
// js代码未改变
var oBox = document.querySelector('#box');
var shadowRoot = oBox.createShadowRoot();
var template = document.querySelector('.box-template');
shadowRoot.appendChild(document.importNode(template.content, true));
在使用多个插入点时,由于每个定义的字段都需要特定的内容,所以我们使用select属性告诉
四、改变顺序
通过插入点,我们不必修改 content 内容的结构而改变渲染的顺序。因为内容存在于影子宿主中,而呈现的方式存在于影子根也就是 shadow DOM 中。
<template class="box-template">
<ul>
<li><content select=".js"></content></li>
<li><content select=".css"></content></li>
<li><content select=".html"></content></li>
<li><content select=""></content></li>
</ul>
</template>
通交换templete中的select值,我们得到了一组重排的数据。
五、贪心插入点
前面的例子中,我们使用
<content></content> <conent select=""></conent> <content select="*"></content>
注:模板标签,这个标签目前在非 IE 的浏览器下都得到支持,主要有四个特性:本文中利用模板为 shadow DOM 填充内容,省去了通过 JS 加载的麻烦。注意文中使用.content 属性来导出模板的内容,并通过importNode()方式对模板进行了深拷贝。
- 惰性:在使用前不会被渲染。
- 无副作用:在使用前,模板内部的各种脚本不会运行、图像不会加载等。
- 内容不可见:模板的内容不存在于文档中,使用选择器无法获取。
- 可被放置于任意位置:即使是 HTML 解析器不允许出现的位置,例如作为